﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// This is a common hysteresis threshold as it may be used in a canny
    /// edge detector, use this with an upper and a lower threshold
    /// </summary>
    public class HysteresisThreshold : BaseForeBackgroundColorFilter
    {
        protected int m_nLower = 200;
        protected int m_nUpper = 255;

        protected int m_nWidth = -1;
        protected int m_nHeight = -1;

        protected UnsafeBitmap m_Input = null;
        protected UnsafeBitmap m_Output = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="HysteresisThreshold"/> class.
        /// </summary>
        public HysteresisThreshold()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HysteresisThreshold"/> class.
        /// </summary>
        public HysteresisThreshold(int _nLower, int _nUpper)
        {
            m_nLower = _nLower;
            m_nUpper = _nUpper;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HysteresisThreshold"/> class.
        /// </summary>
        /// <param name="_hysteresisThreshold">The _hysteresis threshold.</param>
        internal HysteresisThreshold(HysteresisThreshold _hysteresisThreshold)
        {
            m_nLower = _hysteresisThreshold.m_nLower;
            m_nUpper = _hysteresisThreshold.m_nUpper;
            m_nWidth = _hysteresisThreshold.m_nWidth;
            m_nHeight = _hysteresisThreshold.m_nWidth;
        }

        /// <summary>
        /// Gets or sets the lower.
        /// </summary>
        /// <value>The lower.</value>
        public int Lower
        {
            get { return m_nLower; }
            set { m_nLower = value; }
        }

        /// <summary>
        /// Gets or sets the upper.
        /// </summary>
        /// <value>The upper.</value>
        public int Upper
        {
            get { return m_nUpper; }
            set { m_nUpper = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            m_nWidth = _bitmap.Width;
            m_nHeight = _bitmap.Height;
            m_Input = (UnsafeBitmap)_bitmap.Clone();
            m_Output = _bitmap;
            return Process();
        }

        /// <summary>
        /// Processes the specified input.
        /// </summary>
        /// <returns></returns>
        protected UnsafeBitmap Process()
        {
            int x = 0;
            int y = 0;
            for (x = 0; x < m_nWidth; x++)
            {
                for (y = 0; y < m_nHeight; y++)
                {
                    int value = (m_Input[x, y].ToArgb()) & 0xff;
                    if (value >= m_nUpper)
                    {
                        m_Input[x, y] = m_ColorForeground;
                        HysteresisConnect(x, y);
                    }
                }
            }

            for (x = 0; x < m_nWidth; x++)
            {
                for (y = 0; y < m_nHeight; y++)
                {
                    if (m_Input[x, y] == m_ColorForeground)
                    {
                        m_Output[x, y] = m_ColorForeground;
                    }
                    else
                    {
                        m_Output[x, y] = m_ColorBackground;
                    }
                }
            }
            m_Input.UnlockImage();
            return m_Output;
        }

        /// <summary>
        /// Hysts the connect.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        private void HysteresisConnect(int x, int y)
        {
            int nValue = 0;
            int y1 = 0;
            int x1 = 0;
        a:
            for (x1 = x - 1; x1 <= x + 1; x1++)
            {
                for (y1 = y - 1; y1 <= y + 1; y1++)
                {
                    if ((x1 < m_nWidth) & (y1 < m_nHeight) & (x1 >= 0) & (y1 >= 0) & (x1 != x) & (y1 != y))
                    {
                        nValue = (m_Input[x1, y1].ToArgb()) & 0xff;
                        if (nValue != 255)
                        {
                            if (nValue >= m_nLower)
                            {
                                m_Input[x1, y1] = m_ColorForeground;
                                //HysteresisConnect(x1, y1);
                                //TODO... recursively works, but i get stackoverflows then,
                                //if values (lower, upper) differs too much
                                x = x1;
                                y = y1;
                                goto a;
                            }
                            else
                            {
                                m_Input[x1, y1] = m_ColorBackground;
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new HysteresisThreshold(this);
        }
    }
}
